from ....zlgcan.zlgcan import *
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from ....zuds.zuds import *
import json
from collections import namedtuple
from ..models.modle_base_can import FunctestTemplate_Can_Batc
from ....utils.types import UnitTestData_Type, TestData_Type, Uds_Request_Type, UnitTestConfig_Type, Can_Batc_TestDataStucture, UnitTestDataStructure
from ....utils.loggers import dev_logger
from ....utils.utils import executor_pool_exception_callback
from ....utils.types import AlltestReturnType
from concurrent.futures import ThreadPoolExecutor


class Can_Batc_Unittesst_Tab_Frame(tk.Frame):
    def __init__(self, master, masterUI, parentWin):
        super().__init__(master=master)
        self.masterUI = masterUI
        self.parentWin = parentWin
        self.pool = ThreadPoolExecutor(
            max_workers=4, thread_name_prefix="CanFD_Unittest_Tab_frame_pool")
        self.can_unitTabInit()

    def can_unitTabInit(self):

        self.can_unitTabDataInit()
        self.can_unitTabViewInit()
        self.can_unitTab_btn_function_update()
        pass

    def can_unitTestConfigInfo_Import(self):
        self.can_unitTestmodelDict: dict[str, Can_Batc_TestDataStucture] = {}
        with open("config/batc_seat_unittestConfig.json", "r", encoding="utf-8") as fp:
            self.configJson = json.loads(fp.read())

        testmodelList = self.configJson.get('data')
        for item in testmodelList:

            cur = Can_Batc_TestDataStucture(**item)
            self.can_unitTestmodelDict[cur.PosId] = cur
        pass

    def can_unitTabDataInit(self):
        self.can_unitTestConfigInfo_Import()

        self.can_unitTab_UNIT_POS_LIST = []
        self.can_unitTab_UNIT_FUN_DICT = {}
        self.can_unitTab_UNIT_BTN_DICT = {}

        # DOC: 单元测试配置信息
        self.can_unitTab_UNIT_TEST_CONFIG_DICT: dict[str, Can_Batc_TestDataStucture] = {
        }
        MAX_ROW = 8
        MAX_COLUMN = 8
        for i in range(MAX_ROW):
            for j in range(MAX_COLUMN):
                position = f"{i}{j}"
                self.can_unitTab_UNIT_POS_LIST.append(position)

        def default_can_unit_pos_btn_click(i, j):
            return lambda: messagebox.showinfo("提示", f"can_unit R{i}C{j}未定义")
        for pos in self.can_unitTab_UNIT_POS_LIST:
            self.can_unitTab_UNIT_FUN_DICT[pos] = default_can_unit_pos_btn_click
            # [ ] 待更新
            self.can_unitTab_UNIT_TEST_CONFIG_DICT[pos] = Can_Batc_TestDataStucture(PosId=pos, MsgId=0x7ff, rcevId=0x7ff, level="null", level_signal="0000000000000000",  name=f'pos{pos}', testDatas=[]
                                                                                    )

    def can_unitTabViewInit(self):
        for i, j in self.can_unitTab_UNIT_POS_LIST:
            self.can_unitTab_UNIT_BTN_DICT[f"{i}{j}"] = tk.Button(
                self, text=self.can_unitTab_UNIT_TEST_CONFIG_DICT[f'{i}{j}'].name, state=tk.DISABLED, padx=10, pady=5, command=self.can_unitTab_UNIT_FUN_DICT[f'{i}{j}'](i, j))
            self.can_unitTab_UNIT_BTN_DICT[f"{i}{j}"].grid(row=i, column=j)

        # 把配置文件中有定义的按钮设置为可用
        # for pos in self.can_unitTab_UNIT_TEST_CONFIG_JSON_INFO:
            # self.can_unitTab_UNIT_BTN_DICT[pos].configure(state=tk.NORMAL)

        # DOC 调整行列最小间距
        col_count, row_count = self.grid_size()
        for col in range(col_count):
            self.grid_columnconfigure(col, minsize=110)
        for row in range(row_count):
            self.grid_rowconfigure(row, minsize=60)

        self.can_unitTab_btn_function_init()

    def can_unitTab_btn_function_init(self):
        for pos, config in self.can_unitTestmodelDict.items():
            config_dict = config.model_dump()

            def wrapper(pos):
                def inner():
                    # 此处的**关键字参数，不能直接用config_dict，否则所有的实例都是同一个，因为python中是引用传递。所以要通过self.can_unitTestmodelDict[pos]来进行区分
                    return FunctestTemplate_Can_Batc(
                        masterUI=self.masterUI, parentWin=self,  **self.can_unitTestmodelDict[pos].model_dump())
                return inner

            self.can_unitTab_UNIT_FUN_DICT[pos] = wrapper(pos)
            self.can_unitTab_UNIT_BTN_DICT[pos].configure(
                state=tk.NORMAL, text=config_dict['name'], command=wrapper(pos))

    def can_unitTab_btn_function_update(self):
        # 个别测试用例需要手动更新时，可以手工更新
        for pos in self.can_unitTab_UNIT_POS_LIST:
            update_function = getattr(
                self, f'can_unit_function_update_{pos}', None)
            if update_function:
                update_function()

    def can_unit_function_update_02_sp(self):
        pos = '02'
        unitConfig = self.can_unitTestmodelDict[pos].toDict()

        def wrapper():
            # messagebox.showinfo('提示', 'unit02-update......')
            # DOC 此处masterUI指向自己，parentWin也是自己，所以两者是同一个指向对象
            # 如果FunctestTemplate不是通过主窗口调用，而是通过其他Toplevel调用，那么两者可能是不相同的
            return FunctestTemplate_Can_Batc(masterUI=self.masterUI,
                                             parentWin=self, **unitConfig)
        self.can_unitTab_UNIT_BTN_DICT[pos].configure(
            state=tk.NORMAL, text=unitConfig['name'], command=wrapper)

    # 生产单元测试的窗口实例，然后测试完成自动隐藏窗口
    def unitTestCommand(self, posId):
        def wrapper():
            def inner():
                # 设定最大重测次数
                test_counts = 3
                for i in range(test_counts):
                    # testfuncDict里面放的是单元测试的构造函数，运行时候会生产单元测试的面板,frame00就是实例
                    # 后续要将frame00窗口隐藏，以提供更好的体验。
                    frame00 = self.can_unitTab_UNIT_FUN_DICT[posId]()
                    # dev_logger.debug(f'frame00-----{frame00}')

                    # TODO: frame00.allTest就是运行当前单元测试的批量测试， 也可以用btn的invoke模拟按钮被按下来实现(该方法暂未验证)
                    # 此处把allTest的return值拿到，用来在主界面显示
                    testResult_raw = frame00.allTest()

                    # 拿到alltest的结果
                    testResult: AlltestReturnType = testResult_raw.result()
                    resFlag = testResult.resFlag
                    resString = testResult.resString

                    # 测试完成后隐藏窗口
                    frame00.withdraw()

                    if resFlag:
                        return testResult

                # 如果3个循环都走完了，仍然没有True
                return AlltestReturnType(False, '测试失败')

            # FIXME: 此处用一个单独的线程来跑，否则result()函数会把主线程阻塞，导致无法正常工作
            res = self.pool.submit(inner)
            res.add_done_callback(executor_pool_exception_callback)
            return res

        return wrapper
